﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Arroguella.Utilities
{
    /// <summary>
    /// Contains common utility methods used by many different classes
    /// </summary>
    public static class Utils
    {
        #region Drawing

        /// <summary>
        /// Utility method for drawing a rectangle given coordinates and a color.
        /// </summary>
        public static void DrawRectangle(Rectangle coords, Color color, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
        {
            var rect = new Texture2D(graphicsDevice, 1, 1);
            rect.SetData(new[] { color });
            spriteBatch.Draw(rect, coords, color);
        }

        /// <summary>
        /// Will draw a border (hollow rectangle) of given thickness (in pixels)
        /// of the specified color.
        /// </summary>
        public static void DrawRectangleBorder(Texture2D texture, Rectangle rect, int thicknessOfBorder, Color color, SpriteBatch spriteBatch)
        {
            // Draw top line
            spriteBatch.Draw(texture, new Rectangle(rect.X, rect.Y, rect.Width, thicknessOfBorder), color);

            // Draw left line
            spriteBatch.Draw(texture, new Rectangle(rect.X, rect.Y, thicknessOfBorder, rect.Height), color);

            // Draw right line
            spriteBatch.Draw(texture, new Rectangle((rect.X + rect.Width - thicknessOfBorder), rect.Y, thicknessOfBorder, rect.Height), color);

            // Draw bottom line
            spriteBatch.Draw(texture, new Rectangle(rect.X, rect.Y + rect.Height - thicknessOfBorder, rect.Width, thicknessOfBorder), color);
        }

        /// <summary>
        /// Will draw a border (hollow rectangle) of given thickness (in pixels)
        /// of the specified color.
        /// </summary>
        public static void DrawRectangleBorder(Rectangle rect, int thicknessOfBorder, Color color, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
        {
            // Set up the texture based on color
            Texture2D texture = new Texture2D(graphicsDevice, 1, 1);
            texture.SetData(new[] { color });

            DrawRectangleBorder(texture, rect, thicknessOfBorder, color, spriteBatch);
        }

        /// <summary>
        /// Utility method for drawing a background over the whole screen.
        /// </summary>
        public static void DrawBackgroundColor(Color color, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
        {
            DrawRectangle(graphicsDevice.Viewport.TitleSafeArea, color, spriteBatch, graphicsDevice);
        }

        /// <summary>
        /// Utility method for drawing a background over a certain area.
        /// </summary>
        public static void DrawFilledRectangle(Color color, Rectangle rectangle, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
        {
            DrawRectangle(rectangle, color, spriteBatch, graphicsDevice);
        }

        /// <summary>
        /// Draws the given string as large as possible inside the boundaries Rectangle without going
        /// outside of it.  This is accomplished by scaling the string.
        /// </summary>
        public static void DrawString(SpriteBatch spriteBatch, SpriteFont font, string text, Color color, Rectangle boundaries, Alignment alignment)
        {
            if (string.IsNullOrEmpty(text))
            {
                return;
            }

            Vector2 size = font.MeasureString(text);

            float xScale = (boundaries.Width / size.X);
            float yScale = (boundaries.Height / size.Y);

            // Taking the smaller scaling value will result in the text always fitting in the boundaries.
            float scale = Math.Min(xScale, yScale);

            // Alignment
            Vector2 position = new Vector2();
            switch (alignment)
            {
                default:
                case Alignment.Center:
                    {
                        // Figure out the location to absolutely-center it in the boundaries rectangle.
                        int strWidth = (int)Math.Round(size.X * scale);
                        int strHeight = (int)Math.Round(size.Y * scale);
                        position.X = (((boundaries.Width - strWidth) / 2) + boundaries.X);
                        position.Y = (((boundaries.Height - strHeight) / 2) + boundaries.Y);
                        break;
                    }
                case Alignment.Left:
                    {
                        // Figure out the location to left-align it in the boundaries rectangle.
                        int strHeight = (int)Math.Round(size.Y * scale);
                        position.X = boundaries.X;
                        position.Y = (((boundaries.Height - strHeight) / 2) + boundaries.Y);
                        break;
                    }
                case Alignment.Right:
                    {
                        // Figure out the location to right-align it in the boundaries rectangle.
                        int strWidth = (int)Math.Round(size.X * scale);
                        int strHeight = (int)Math.Round(size.Y * scale);
                        position.X = (boundaries.Width - strWidth) + boundaries.X;
                        position.Y = (((boundaries.Height - strHeight) / 2) + boundaries.Y);
                        break;
                    }
            }

            // A bunch of settings where we just want to use reasonable defaults.
            float rotation = 0.0f;
            Vector2 spriteOrigin = new Vector2(0, 0);
            float spriteLayer = 0.0f; // all the way in the front
            SpriteEffects spriteEffects = new SpriteEffects();

            // Draw the string to the sprite batch!
            spriteBatch.DrawString(font, text, position, color, rotation, spriteOrigin, scale, spriteEffects, spriteLayer);
        }

        /// <summary>
        /// Draws the given string as large as possible inside the boundaries Rectangle without going
        /// outside of it.  This is accomplished by scaling the string (since the SpriteFont has a specific
        /// size).
        /// 
        /// If the string is not a perfect match inside of the boundaries (which it would rarely be), then
        /// the string will be absolutely-centered inside of the boundaries.
        /// </summary>
        public static void DrawString(SpriteBatch spriteBatch, SpriteFont font, string text, Color color, Rectangle boundaries)
        {
            if (string.IsNullOrEmpty(text))
            {
                return;
            }

            Vector2 size = font.MeasureString(text);

            float xScale = (boundaries.Width / size.X);
            float yScale = (boundaries.Height / size.Y);

            // Taking the smaller scaling value will result in the text always fitting in the boundaries.
            float scale = Math.Min(xScale, yScale);

            // Figure out the location to absolutely-center it in the boundaries rectangle.
            int strWidth = (int)Math.Round(size.X * scale);
            int strHeight = (int)Math.Round(size.Y * scale);
            Vector2 position = new Vector2();
            position.X = (((boundaries.Width - strWidth) / 2) + boundaries.X);
            position.Y = (((boundaries.Height - strHeight) / 2) + boundaries.Y);

            // A bunch of settings where we just want to use reasonable defaults.
            float rotation = 0.0f;
            Vector2 spriteOrigin = new Vector2(0, 0);
            float spriteLayer = 0.0f; // all the way in the front
            SpriteEffects spriteEffects = new SpriteEffects();

            // Draw the string to the sprite batch!
            spriteBatch.DrawString(font, text, position, color, rotation, spriteOrigin, scale, spriteEffects, spriteLayer);
        }

        /// <summary>
        /// For a given rectangle, takes the text and inserts newline characters between words,
        /// such that the text does not ever break the boundaries of the rectangle.
        /// Then it draws the text.
        /// </summary>
        public static void DrawStringWrappedInRectangle(string text, Rectangle rect, Color color, SpriteFont font, SpriteBatch spriteBatch)
        {
            string wrappedText = WrapStringToRectangle(text, rect, font);
            Vector2 position = new Vector2(rect.X, rect.Y);
            spriteBatch.DrawString(font, wrappedText, position, color);
        }

        /// <summary>
        /// For a given rectangle, takes the text and inserts newline characters between words,
        /// such that the text does not ever break the boundaries of the rectangle.
        /// 
        /// NOTE: SpriteFont.MeasureString().Length() returns the diagonal lenth of the string.
        ///       Always use SpriteFont.MeasureString().X to get the true horizontal length of the string.
        /// </summary>
        private static string WrapStringToRectangle(string text, Rectangle rect, SpriteFont font)
        {
            String line = String.Empty;
            String returnString = String.Empty;
            String[] wordArray = text.Split(' ');

            foreach (String word in wordArray)
            {
                if (font.MeasureString(line + word).X > rect.Width + 20)
                {
                    returnString = returnString + line + '\n';
                    line = String.Empty;
                }

                line = line + word + ' ';
            }

            return returnString + line;
        }

        #endregion

        #region Enums

        public enum Alignment
        {
            Left,
            Center,
            Right
        };

        #endregion
    }
}
